import types
import pytest

from sqlmigration.validation import (
    parse_mysql_source,
    validate_counts_against_source,
    validate_structure_against_source,
    render_report_text,
)


def _sep(title: str, start: bool):
    print("\n" + "-" * 72)
    print(f"{title} — {'START' if start else 'END'}")
    print("-" * 72)


class FakeDB:
    def __init__(self, table_counts: dict[str, int], tables_present: set[str], idx_per_table: dict[str, int], fk_per_table: dict[str, int], seq_per_table: dict[str, int]):
        self._counts = dict(table_counts)
        self._present = set(tables_present)
        self._idx = dict(idx_per_table)
        self._fk = dict(fk_per_table)
        self._seq = dict(seq_per_table)

    def fetch_table_row_counts(self, tables: list[str]) -> dict[str, int]:
        return {t: int(self._counts.get(t, -1)) for t in tables}

    def fetch_scalar(self, sql: str):
        s = sql.lower()
        if "current_database()" in s:
            return "kwdb"
        # index count: support both "from pg_catalog.pg_index" and "join pg_index"
        if (" pg_index " in s) or ("from pg_catalog.pg_index" in s):
            # extract table name using lower(relname) = 'table'
            import re
            m = re.search(r"lower\(t\.relname\)\s*=\s*'([^']+)'", s)
            if m:
                return int(self._idx.get(m.group(1), 0))
            return 0
        if "constraint_type = 'foreign key'" in s:
            import re
            m = re.search(r"lower\(table_name\)\s*=\s*'([^']+)'", s)
            if m:
                return int(self._fk.get(m.group(1), 0))
            return 0
        if "from information_schema.sequences" in s:
            import re
            m = re.search(r"like\s*'([^']+)_\%_seq'", s)
            if m:
                base = m.group(1)
                return int(self._seq.get(base, 0))
            return 0
        if "select exists" in s and "from pg_class" in s:
            import re
            m = re.search(r"lower\(t\.relname\)\s*=\s*'([^']+)'", s)
            if m:
                return m.group(1) in self._present
            return False
        return None

    def fetch_all(self, sql: str):
        s = sql.lower()
        if "from pg_class t join pg_namespace" in s:
            return [(t,) for t in sorted(self._present)]
        return []


def test_validation_counts_and_structure_success():
    _sep("[CASE-VALID] 源文件 vs 目标库 验证 — 成功路径", True)
    mysql_src = (
        "CREATE DATABASE `shop`;\n"
        "CREATE TABLE `t1`(id int primary key, v int, UNIQUE KEY uq_v (v));\n"
        "CREATE TABLE `t2`(id int primary key, pid int, FOREIGN KEY (pid) REFERENCES `t1`(id));\n"
        "INSERT INTO `t1` VALUES (1,10), (2,20);\n"
        "INSERT INTO `t2` VALUES (1,1);\n"
    )
    src_meta = parse_mysql_source(mysql_src)
    print("[INPUT] MySQL META:", src_meta)

    # Target side fakes: match the source
    table_counts = {"t1": 2, "t2": 1}
    tables_present = {"t1", "t2"}
    idx_per_table = {"t1": int(src_meta["t1"].get("indexes", 0)), "t2": int(src_meta["t2"].get("indexes", 0))}
    fk_per_table = {"t1": int(src_meta["t1"].get("fks", 0)), "t2": int(src_meta["t2"].get("fks", 0))}
    seq_per_table = {"t1": int(src_meta["t1"].get("sequences", 0)), "t2": int(src_meta["t2"].get("sequences", 0))}
    db = FakeDB(table_counts, tables_present, idx_per_table, fk_per_table, seq_per_table)

    rpt_counts = validate_counts_against_source(db, src_meta)
    rpt_struct = validate_structure_against_source(db, src_meta)
    print("[RESULT] counts.ok=", rpt_counts.ok)
    print("[RESULT] struct.ok=", rpt_struct.ok)
    txt = render_report_text([rpt_counts, rpt_struct])
    print("[RESULT] Report Text:\n" + txt)

    assert rpt_counts.ok is True
    assert rpt_struct.ok is True
    _sep("[CASE-VALID] 源文件 vs 目标库 验证 — 成功路径", False)
